package jstudy.gu.kit.reflect;

import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;

public final class ReflectKit {

	private static Map<Class<?>, Map<String, Field>> cache = new HashMap<>(); // 内存占用多,可以用softReferenceCache 来避免oom

	public static void main(String[] args) {
		System.out.println(List.class);
		System.out.println(new ArrayList<>().getClass());

		Source s = new Source().setName("s1").setSource("ssss").setId("id").setList(new LinkedList<>());
		Target s2 = new Target().setTarget("tar").setId(44);
		System.out.println(s);
		System.out.println(s2);
		copySameField(s, s2);
		System.out.println("----------");
		System.out.println(s);
		System.out.println(s2);

		// Bean -> Map
		// bean -> 获取副本 ,获取 copy
		// 将bean1 的值按field 名称设置给对应的 bean2
		// 关键代码,检查指定的值,是否可以设置给字段if (field.getType().isAssignableFrom(value.getClass()))
		// {
	}

	public static <T> T newObject(Class<T> clz) {
		try {
			Constructor<?> cs = clz.getConstructor();
			cs.setAccessible(true);
			return (T) cs.newInstance();
		} catch (Exception e) {
			e.printStackTrace();
		}
		return null;
	}

	public static Map<String, Object> toMap(Object o) {
		if (o == null) {
			return new HashMap<>();
		}
		Map<String, Object> rmap = new HashMap<>();
		if (o instanceof Map) {
			Map<?, ?> omap = (Map<?, ?>) o;
			omap.forEach((k, v) -> rmap.put(String.valueOf(k), v));

		} else {
			getNotStatisFields(o).forEach((fname, field) -> {
				rmap.put(fname, getFieldValue(field, o));
			});
		}
		return rmap;
	}

	public static Map<String, Object> toMapNotNull(Object o) {
		if (o == null) {
			return new HashMap<>();
		}
		Map<String, Object> rmap = new HashMap<>();
		if (o instanceof Map) {
			Map<?, ?> omap = (Map<?, ?>) o;
			omap.forEach((k, v) -> rmap.put(String.valueOf(k), v));

		} else {
			getNotStatisFields(o).forEach((fname, field) -> {
				Object value = getFieldValue(field, o);
				if (value != null)
					rmap.put(fname, value);
			});
		}
		return rmap;
	}

	/**
	 * @author guweichao 将source的值,按照字段名称匹配,设置给target
	 * @created_at 2021年1月29日 下午8:18:21
	 * @param <T>
	 * @param <S>
	 * @param source
	 * @param target
	 */
	public static <T, S> void copySameField(S source, T target) {
		if (source.getClass() == target.getClass()) {
			copySameField(source, (S) target, true);
		} else {
			Map<String, Field> targetFs = getNotStatisFields(target);
			Map<String, Field> sourceFs = getNotStatisFields(source);
			targetFs.forEach((fname, field) -> {
				Field sourceF = sourceFs.get(fname);
				Object sourceValue = getFieldValue(sourceF, source);
				setFieldValue(field, target, sourceValue);
			});
		}
	}

	public static <T, S> void copySameFieldNotNull(S source, T target) {
		if (source.getClass() == target.getClass()) {
			copySameField(source, target, false);
		} else {
			Map<String, Field> targetFs = getNotStatisFields(target);
			Map<String, Field> sourceFs = getNotStatisFields(source);
			targetFs.forEach((fname, field) -> {
				Field sourceF = sourceFs.get(fname);
				Object sourceValue = getFieldValue(sourceF, source);
				if (sourceValue != null)
					setFieldValue(field, target, sourceValue);
			});
		}
	}

	private static <T> void copySameField(T source, T target, boolean copyNull) {
		Map<String, Field> fsmap = getNotStatisFields(source);
		fsmap.forEach((fname, field) -> {
			Object value = getFieldValue(field, source);
			if (value != null || copyNull)
				setFieldValueWithOutCheck(field, target, value);
		});
	}

	private static Map<String, Field> getNotStatisFields(Object o) {
		Map<String, Field> clzMap = cache.computeIfAbsent(o.getClass(), k -> {
			Map<String, Field> fmap = new HashMap<>();
			Field[] fs = k.getDeclaredFields();
			for (Field f : fs) {
				if (!Modifier.isStatic(f.getModifiers())) {// 非静态字段
					f.setAccessible(true);
					fmap.put(f.getName(), f);
				}
			}
			return fmap;
		});
		return clzMap;
	}

	private static Field getField(Object o, String name) {
		Map<String, Field> clzMap = getNotStatisFields(o);
		return clzMap.get(name); // 这种可能是null
	}

	public static void setFieldValue(Object o, String fieldName, Object value) {
		Field field = getField(o, fieldName);
		if (field != null) {
			try {
				field.set(o, value);
			} catch (IllegalArgumentException e) {
				e.printStackTrace();
			} catch (IllegalAccessException e) {
				e.printStackTrace();
			}
		}
	}

	/**
	 * @author guweichao
	 * @created_at 2021年1月27日 下午7:07:45
	 * @param o
	 * @param fieldName
	 * @return
	 * 
	 *         对于getField , 关键方法是 1.clz.getDeclaredField(fieldName) 获取字段
	 *         2.field.setAccessible(true); // 设置访问权限
	 */
	public static <T> T getFieldValue(Object o, String fieldName) {
		Field field = getField(o, fieldName);
		return (T) getFieldValue(field, o);
	}

	private static <T> T getCopy(T o) {
		if (o == null) {
			return null;
		}
		T copy = (T) newObject(o.getClass());
		copySameFieldNotNull(o, copy);
		return copy;

	}

	private static Object getFieldValue(Field field, Object o) {
		if (field != null) {
			try {
				return field.get(o);
			} catch (IllegalArgumentException e) {
				e.printStackTrace();
			} catch (IllegalAccessException e) {
				e.printStackTrace();
			}
		}
		return null;
	}

	private static void setFieldValue(Field field, Object o, Object value) {
		if (field != null) {
			try {
				if (value == null) {
					field.set(o, value);
				} else {
					if (field.getType().isAssignableFrom(value.getClass())) {
//					if (field.getType()==value.getClass()) {
						field.set(o, value);
					}
				}
			} catch (IllegalArgumentException e) {
				e.printStackTrace();
			} catch (IllegalAccessException e) {
				e.printStackTrace();
			}
		}
	}

	private static void setFieldValueWithOutCheck(Field field, Object o, Object value) {
		if (field != null) {
			try {
				field.set(o, value);
			} catch (IllegalArgumentException e) {
				e.printStackTrace();
			} catch (IllegalAccessException e) {
				e.printStackTrace();
			}
		}
	}
}
